////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2005.
// -------------------------------------------------------------------------
//  File name:   FacialModel.cpp
//  Version:     v1.00
//  Created:     10/10/2005 by Timur.
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "FacialModel.h"
#include "FaceAnimation.h"
#include "FaceEffectorLibrary.h"

#include <I3DEngine.h>
#include <CryHeaders.h>
#include "../CharacterInstance.h"
#include "../ModelMesh.h"
#include "../Model.h"

#undef PI
#define PI 3.14159265358979323f

//////////////////////////////////////////////////////////////////////////
CFacialModel::CFacialModel( CCharacterModel *pModel )
{
	m_pModel = pModel;
	InitMorphTargets();
	m_bAttachmentChanged = false;
}

//////////////////////////////////////////////////////////////////////////
CFaceState* CFacialModel::CreateState()
{
	CFaceState *pState = new CFaceState(this);
	if (m_pEffectorsLibrary)
		pState->SetNumWeights( m_pEffectorsLibrary->GetLastIndex() );
	return pState;
}

//////////////////////////////////////////////////////////////////////////
void CFacialModel::InitMorphTargets()
{
	CModelMesh *pModelMesh = m_pModel->GetModelMesh(0);
	int nMorphTargetsCount = (int)pModelMesh->m_morphTargets.size();
	m_effectors.reserve(nMorphTargetsCount);
	m_morphTargets.reserve( nMorphTargetsCount );
	for (int i = 0; i < nMorphTargetsCount; i++)
	{
		CMorphTarget *pMorphTarget = pModelMesh->m_morphTargets[i];
		if (!pMorphTarget)
			continue;

		CFacialMorphTarget *pMorphEffector = new CFacialMorphTarget(i);
		string morphname = pMorphTarget->m_name;
		if (!morphname.empty() && morphname[0] == '#') // skip # character at the begining of the morph target.
			morphname = morphname.substr(1);

		CFacialAnimation* pFaceAnim = g_pCharacterManager->GetFacialAnimation();
		if (!pFaceAnim)
			return;
		pMorphEffector->SetIdentifier(pFaceAnim->CreateIdentifierHandle(morphname));
		pMorphEffector->SetIndexInState(i);

		SEffectorInfo ef;
		ef.pEffector = pMorphEffector;
		ef.nMorphTargetId = i;
		m_effectors.push_back( ef );

		SMorphTargetInfo mrphTrg;
		morphname.MakeLower();
		mrphTrg.name = pFaceAnim->CreateIdentifierHandle(morphname);
		mrphTrg.nMorphTargetId = i;
		m_morphTargets.push_back( mrphTrg );
	}

	std::sort( m_morphTargets.begin(),m_morphTargets.end() );

	//m_libToFaceState
}

//////////////////////////////////////////////////////////////////////////
void CFacialModel::AssignLibrary( IFacialEffectorsLibrary *pLibrary )
{
	assert( pLibrary );
	if (pLibrary == m_pEffectorsLibrary)
		return;

	m_pEffectorsLibrary = (CFacialEffectorsLibrary*)pLibrary;

	// Adds missing morph targets to the Facial Library.
	uint32 numEffectors = m_effectors.size();
	for (uint32 i=0; i<numEffectors; i++)
	{
		CFacialEffector *pModelEffector = m_effectors[i].pEffector;

		CFacialEffector *pEffector = (CFacialEffector*)m_pEffectorsLibrary->Find( pModelEffector->GetIdentifier() );
		if (!pEffector)
		{
			// Library does not contain this effector, so add it into the library.
			if (m_effectors[i].nMorphTargetId >= 0)
			{
				IFacialEffector* pNewEffector = m_pEffectorsLibrary->CreateEffector( EFE_TYPE_MORPH_TARGET,pModelEffector->GetIdentifier() );
				IFacialEffector* pRoot = m_pEffectorsLibrary->GetRoot();
				if (pRoot && pNewEffector)
					pRoot->AddSubEffector(pNewEffector);
			}
		}
	}

	// Creates a mapping table between Face state index to the index in MorphTargetsInfo array.
	// When face state is applied to the model, this mapping table is used to find what morph target is associated with state index.
	int nIndices = m_pEffectorsLibrary->GetLastIndex();
	m_faceStateIndexToMorphTargetId.resize(nIndices);
	for (int i = 0; i < nIndices; i++)
	{
		// Initialize all face state mappings.
		m_faceStateIndexToMorphTargetId[i] = -1;
	}
	SMorphTargetInfo tempMT;
	for (CFacialEffectorsLibrary::CrcToEffectorMap::iterator it = m_pEffectorsLibrary->m_crcToEffectorMap.begin(); it != m_pEffectorsLibrary->m_crcToEffectorMap.end(); ++it)
	{
		CFacialEffector *pEffector = it->second;

		int nStateIndex = pEffector->GetIndexInState();
		if (nStateIndex >= 0 && nStateIndex < nIndices)
		{
			// Find morph target for this state index.
			// Morph targets must be sorted by name for binary_find to work correctly.
			tempMT.name = pEffector->GetIdentifier();

			MorphTargetsInfo::iterator it2 = stl::binary_find( m_morphTargets.begin(),m_morphTargets.end(),tempMT );
			if (it2 != m_morphTargets.end())
			{
				m_faceStateIndexToMorphTargetId[nStateIndex] = (int)std::distance(m_morphTargets.begin(),it2);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
IFacialEffectorsLibrary* CFacialModel::GetLibrary()
{
	return m_pEffectorsLibrary;
}

//////////////////////////////////////////////////////////////////////////
int CFacialModel::GetMorphTargetCount() const
{
	return m_morphTargets.size();
}

//////////////////////////////////////////////////////////////////////////
const char* CFacialModel::GetMorphTargetName(int morphTargetIndex) const
{
#ifdef FACE_STORE_ASSET_VALUES
	return (morphTargetIndex >= 0 && morphTargetIndex < int(m_morphTargets.size()) ? m_morphTargets[morphTargetIndex].name.GetString().c_str() : 0);
#else
	CryFatalError("Not Supported on this platform.");
	return NULL;
#endif
}

void CFacialModel::PushMorphEffectors(int i, const MorphTargetsInfo& morphTargets, f32 fWeight, f32 fBalance, DynArray<CryModEffMorph>& arrMorphEffectors )
{
		// This is a morph target.
		int nMorphIndex = m_faceStateIndexToMorphTargetId[i];
		if (nMorphIndex >= 0 && nMorphIndex < (int)m_morphTargets.size())
		{
			int nMorphTargetId = m_morphTargets[nMorphIndex].nMorphTargetId;

			// Add morph target to character.
			CryModEffMorph morph;
			morph.m_fTime = 0;
			morph.m_nFlags = CryCharMorphParams::FLAGS_FREEZE | CryCharMorphParams::FLAGS_NO_BLENDOUT | CryCharMorphParams::FLAGS_INSTANTANEOUS;
			morph.m_nMorphTargetId = nMorphTargetId;
			morph.m_Params.m_fAmplitude = fWeight;
			morph.m_Params.m_fBlendIn = 0;
			morph.m_Params.m_fBlendOut = 0;
			morph.m_Params.m_fBalance = fBalance;
			arrMorphEffectors.push_back(morph);
		}
}


//////////////////////////////////////////////////////////////////////////
void CFacialModel::FillInfoMapFromFaceState(CFacialDisplaceInfo& info, CFaceState* const pFaceState, DynArray<CryModEffMorph>& arrMorphEffectors, CSkeletonPose* const pSkeletonPose, int numForcedRotations, const CFacialAnimForcedRotationEntry* const forcedRotations, float blendBoneRotations )
{
	DEFINE_PROFILER_FUNCTION();

	if (!m_pEffectorsLibrary)
		return;
	
	// Update Face State with the new number of indices.
	if (pFaceState->GetNumWeights() != m_pEffectorsLibrary->GetLastIndex())
	{
		pFaceState->SetNumWeights( m_pEffectorsLibrary->GetLastIndex() );
	}
	
	const bool blendBones = (blendBoneRotations > 0.0f);

	const size_t infoDisplaceInfoMapSize = info.GetCount();
	s_boneInfoBlending.Initialize(infoDisplaceInfoMapSize);
	if (blendBones)
	{
		s_boneInfoBlending.CopyUsed(info);
	}
	else
	{
		info.Clear();
	}

	int32 nNumIndices = pFaceState->GetNumWeights();
	for (int32 i = 0; i < nNumIndices; i++)
	{
		f32 fWeight = pFaceState->GetWeight(i);
		f32 fWeightAbs = (f32)__fsel(fWeight, fWeight, -fWeight);

		if (fWeightAbs < EFFECTOR_MIN_WEIGHT_EPSILON)
			continue;

		f32 fBalance = pFaceState->GetBalance(i);

		if (i < (int)m_faceStateIndexToMorphTargetId.size() && m_faceStateIndexToMorphTargetId[i] >= 0)
		{
			PushMorphEffectors(i, m_morphTargets, fWeight, fBalance, arrMorphEffectors);
		}
		else
		{
			CFacialEffector *pEffector = m_pEffectorsLibrary->GetEffectorFromIndex(i);
			if (pEffector)
			{
				// Not a morph target.
				switch (pEffector->GetType())
				{
				case EFE_TYPE_ATTACHMENT:
					CryFatalError("Attachment Effectors not longer supported!");
					break;
				case EFE_TYPE_BONE:
					{
						QuatT qt;
						const int32 boneID = ApplyBoneEffector(pSkeletonPose, pEffector, fWeight, qt);
						if (boneID >= 0)
						{
							QuatT boneInfo = s_boneInfoBlending.GetDisplaceInfo(boneID);
							{
								boneInfo.q += qt.q;
								boneInfo.t += qt.t;
								s_boneInfoBlending.SetDisplaceInfo(boneID, boneInfo);
							}
						}
					}
					break;
				}
			}
		}
	}

	{
		const float blendWeight = (1.f - blendBoneRotations);
		for (size_t i = 0; i < infoDisplaceInfoMapSize; ++i)
		{
			const bool isUsed = s_boneInfoBlending.IsUsed(i);
			if (isUsed)
			{
				const QuatT& blendBoneInfo = s_boneInfoBlending.GetDisplaceInfo(i);
				QuatT boneInfo = info.GetDisplaceInfo(i);

				boneInfo.SetNLerp(boneInfo, blendBoneInfo, blendWeight);
				boneInfo.q.Normalize();

				info.SetDisplaceInfo(i, boneInfo);
			}
		}
	}

	// Apply forced rotations if any. This is currently used only in the facial editor preview mode to
	// support the code interface to force the neck and eye orientations.
	IF_UNLIKELY(forcedRotations && pSkeletonPose)
	{
		for (int rotIndex = 0; rotIndex < numForcedRotations; ++rotIndex)
		{
			int32 id=forcedRotations[rotIndex].jointId;
			if (id >= 0 && id < MAX_JOINT_AMOUNT)
			{
				QuatT qt = info.GetDisplaceInfo(id);
				qt = qt * forcedRotations[rotIndex].rotation;
				qt.q.Normalize();

				info.SetDisplaceInfo(id, qt);
			}
		}
	}

	TransformTranslations(info, pSkeletonPose);
}

void CFacialModel::TransformTranslations(CFacialDisplaceInfo& info, const CSkeletonPose* const pSkeletonPose)
{
	const size_t infoCount = info.GetCount();
	for (size_t i = 0; i < infoCount; ++i)
	{
		const bool isUsed = info.IsUsed(i);
		if (isUsed)
		{
			const int32 p = pSkeletonPose->m_parrModelJoints[i].m_idxParent;
			QuatT q = info.GetDisplaceInfo(i);
			q.t = !pSkeletonPose->GetPoseData().m_pJointsAbsolute[p].q * q.t;
			info.SetDisplaceInfo(i, q);
		}
	}
}

void CFacialModel::ApplyDisplaceInfoToJoints(const CFacialDisplaceInfo& info, const CModelSkeleton* const pSkeleton, Skeleton::CPoseData* const pPoseData, const QuatT* const pJointsRelativeDefault)
{
	DEFINE_PROFILER_FUNCTION();
	uint32 numJoints = pPoseData->GetJointCount();

	// for dirty joints, the absolute pose has to be recalculated -- see below
	uint32 dirty[MAX_JOINT_AMOUNT];
	uint32 firstDirty = MAX_JOINT_AMOUNT;
	memset(dirty, 0x00, sizeof(uint32) * numJoints);

	const uint32 infoCount = static_cast<uint32>(info.GetCount());
	for (uint32 i = 1; i < infoCount; ++i)
	{
		const bool isUsed = info.IsUsed(i);
		if (isUsed)
		{
			const QuatT& jointFacialRelative = info.GetDisplaceInfo(i);
			const QuatT& jointDefaultRelative = pJointsRelativeDefault[i];
			pPoseData->m_pJointsRelative[i] = jointDefaultRelative * jointFacialRelative;

			dirty[i] = true;
			firstDirty = min(firstDirty, i);
		}
	}

	assert( firstDirty >= 1 && firstDirty < MAX_JOINT_AMOUNT );

	for (uint32 i = firstDirty; i < numJoints; ++i)
	{

		int p = pSkeleton->m_arrModelJoints[i].m_idxParent;
		assert(p>=0 && p<MAX_JOINT_AMOUNT);
		if (dirty[p])
		{
			dirty[i] = dirty[p];
			pPoseData->GetJointsAbsolute()[i] = pPoseData->GetJointsAbsolute()[p] * pPoseData->GetJointsRelative()[i];
		}
	}
}

//////////////////////////////////////////////////////////////////////////
int32 CFacialModel::ApplyBoneEffector( CSkeletonPose* pSkeletonPose, CFacialEffector *pEffector,float fWeight,QuatT& quatT)
{
	uint32 crc32 = pEffector->GetAttachmentCRC();
	int nBoneId = pSkeletonPose->GetJointIDByCRC32(crc32);

	if (nBoneId >= 0)
	{
		QuatT qt = pEffector->GetQuatT();
		qt.t *= fWeight;

		if (fWeight > 0)
			qt.q = Quat(IDENTITY) * (1.0f - fWeight) + qt.q * fWeight;
		else
			qt.q = Quat(IDENTITY) * (1.0f + fWeight) + !qt.q * (-fWeight);

		quatT = qt;
		return nBoneId;
	}
	return -1;
}

size_t CFacialModel::SizeOfThis()
{
	size_t nSize(sizeof(CFacialModel));
	nSize += sizeofVector(m_effectors);
	nSize += sizeofVector(m_faceStateIndexToMorphTargetId);
	nSize += sizeofVector(m_morphTargets);

	return nSize;
}

void CFacialModel::GetMemoryUsage(ICrySizer *pSizer) const
{
	pSizer->AddObject(this, sizeof(*this));
	pSizer->AddObject(m_effectors);
	pSizer->AddObject(m_pEffectorsLibrary);
	pSizer->AddObject(m_faceStateIndexToMorphTargetId);
	pSizer->AddObject(m_morphTargets);
}

void CFacialModel::ClearResources()
{
	stl::free_container(s_boneInfoBlending);
}

CFacialDisplaceInfo CFacialModel::s_boneInfoBlending;

#include UNIQUE_VIRTUAL_WRAPPER(IFacialModel)
